---
title: Bottom Sheet
description: Using the Bottom Sheet machine in your project.
package: "@zag-js/bottom-sheet"
---

A bottom sheet is a panel that slides up from the bottom of the screen, often
used in mobile applications to present additional content or actions.

<Resources pkg="@zag-js/bottom-sheet" />

<Showcase id="BottomSheet" />

**Features**

- Supports modal and non-modal modes
- Focus is trapped and scrolling is blocked in modal mode
- Supports customizable snap points for partial or full expansion
- Allows dragging to adjust the sheet's height or swipe to dismiss
- Provides screen reader announcements via rendered title
- Pressing `Esc` closes the bottom sheet
- Supports touch gestures for smooth interaction on mobile devices

## Installation

To use the bottom sheet machine in your project, run the following command in
your command line:

<CodeSnippet id="bottom-sheet/installation.mdx" />

## Anatomy

To use the bottom sheet component correctly, you'll need to understand its
anatomy and how we name its parts.

> Each part includes a `data-part` attribute to help identify them in the DOM.

<Anatomy id="bottom-sheet" />

## Usage

First, import the bottom sheet package into your project:

```jsx
import * as bottomSheet from "@zag-js/bottom-sheet"
```

The bottom sheet package exports two key functions:

- `machine` — The state machine logic for the bottom sheet widget, supporting
  WAI-ARIA specifications and mobile-friendly interactions.
- `connect` — The function that translates the machine's state to JSX attributes
  and event handlers.

> You'll need to provide a unique `id` to the `useMachine` hook. This is used to
> ensure that every part has a unique identifier.

Next, import the required hooks and functions for your framework and use the
bottom sheet machine in your project 🔥

<CodeSnippet id="bottom-sheet/usage.mdx" />

### Controlled bottom sheet

To control the bottom sheet's open state, pass the `open` and `onOpenChange`
properties.

<CodeSnippet id="bottom-sheet/controlled.mdx" />

### Listening for open state changes

When the bottom sheet state changes, the `onOpenChange` callback is invoked.

```jsx {3-7}
const service = useMachine(bottomSheet.machine, {
  id: useId(),
  onOpenChange(details) {
    // details => { open: boolean }
    console.log("bottom sheet open:", details.open)
  },
})
```

### Configuring snap points

The bottom sheet supports customizable snap points, which determine the heights
at which the sheet can "snap" when dragged. Snap points can be defined as
numbers (representing percentages of the content height, e.g., `0.5` for 50%) or
pixel values (e.g., `"200px"`). By default, it snaps to full sheet height (`1`).

```jsx
const service = useMachine(bottomSheet.machine, {
  id: useId(),
  snapPoints: [0.3, 0.5, 1], // 30%, 50%, and 100% of sheet height
  defaultActiveSnapPoint: 0.5, // Start at 50% height
  onActiveSnapPointChange: ({ snapPoint }) => {
    console.log("Active snap point:", snapPoint)
  },
})
```

### Swipe-to-dismiss behavior

The bottom sheet can be dismissed by swiping it down quickly or dragging it
below a threshold. Customize this behavior with the following properties:

- `swipeVelocityThreshold`: The minimum velocity (in pixels per second) required
  to dismiss on swipe (default: `500`).
- `closeThreshold`: The percentage of content height below which the sheet
  closes when dragged (default: `0.25`).

```jsx {3-4}
const service = useMachine(bottomSheet.machine, {
  id: useId(),
  swipeVelocityThreshold: 800,
  closeThreshold: 0.3,
})
```

### Preventing dragging during scroll

By default, dragging is prevented if the content inside the bottom sheet is
scrollable and not at the top or bottom. To allow dragging even during
scrolling, set `preventDragOnScroll` to `false`:

```jsx {3}
const service = useMachine(bottomSheet.machine, {
  id: useId(),
  preventDragOnScroll: false,
})
```

## Styling guide

Earlier, we mentioned that each bottom sheet part has a `data-part` attribute
added to them to select and style them in the DOM.

### Open and closed state

The bottom sheet has three states: `open`, `closed`, and `dragging`. Use the
`data-state` attribute to style based on these states:

```css
[data-part="content"][data-state="open|closed"] {
  /* styles for the content in different states */
}

[data-part="trigger"][data-state="open|closed"] {
  /* styles for the trigger in different states */
}
```

### Animating the bottom sheet

```css
@keyframes slideIn {
  from {
    transform: translate3d(0, 100%, 0);
  }
  to {
    transform: translate3d(0, var(--bottom-sheet-translate, 0), 0);
  }
}

@keyframes slideOut {
  from {
    transform: translate3d(0, var(--bottom-sheet-translate, 0), 0);
  }
  to {
    transform: translate3d(0, 100%, 0);
  }
}

[data-scope="bottom-sheet"][data-part="content"][data-state="open"] {
  animation: slideIn 500ms cubic-bezier(0.32, 0.72, 0, 1);
}

[data-scope="bottom-sheet"][data-part="content"][data-state="closed"] {
  animation: slideOut 500ms cubic-bezier(0.32, 0.72, 0, 1);
}
```

## Methods and Properties

The bottom sheet's `api` exposes the following methods and properties:

### Machine Context

The bottom sheet machine exposes the following context properties:

<ContextTable name="bottom-sheet" />

### Machine API

The bottom sheet `api` exposes the following methods:

<ApiTable name="bottom-sheet" />

### Data Attributes

<DataAttrTable name="bottom-sheet" />

### CSS Variables

<CssVarTable name="bottom-sheet" />
